package com.gitee.wsl.ext.array

import com.gitee.wsl.transform.encode.hex.HexUtil
import kotlinx.io.Buffer
import kotlinx.io.readByteArray
import kotlin.experimental.and
import kotlin.experimental.or



/*
fun ByteArray.toHex(): String = this.joinToString(separator = " ") { String.format("%02X", it) }
@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.toHex(): String = this.joinToString(separator = " ") { String.format("%02X", it.toByte()) }
*/

fun buildByteArray(write: Buffer.()->Unit): ByteArray{
    return Buffer().use{
        it.write()
        it.readByteArray()
    }
}

fun ByteArray.xor(key: String) =
    mapIndexed { index, c -> (c.toInt() xor key[index % key.length].code).toByte() }.toByteArray()

fun ByteArray.xor(key: ByteArray) =
    mapIndexed { index, c -> (c.toInt() xor key[index % key.size].toInt()).toByte() }.toByteArray()

fun ByteArray.leftPaddedCopyOf(n: Int): ByteArray {
    if (size >= n) return copyOf()

    val ret = ByteArray(n)
    val pad = n - size
    repeat(size) { ret[pad + it] = this[it] }

    return ret
}

private fun ByteArray.checkSizeEquals(other: ByteArray) {
    check(size == other.size) { "Byte arrays have different sizes (this: $size, other: ${other.size})" }
}


infix fun ByteArray.or(other: ByteArray): ByteArray {
    checkSizeEquals(other)
    return ByteArray(size) { this[it] or other[it] }
}

infix fun ByteArray.and(other: ByteArray): ByteArray {
    checkSizeEquals(other)
    return ByteArray(size) { this[it] and other[it] }
}


internal val emptyByteArray = ByteArray(0)

fun ByteArray.subArray(newSize: Int): ByteArray {
    require(size >= 0)
    if (isEmpty()) return emptyByteArray
    require(newSize <= size)
    if (newSize == size) return this
    return copyOf(newSize)
}

infix fun ByteArray.concat(append: ByteArray): ByteArray {
    if (this.isEmpty()) return append
    if (append.isEmpty()) return this
    return this + append
}

private val almostEmptyArray = ByteArray(1)

//this hack will be dropped with introducing of new IO or functions APIs
fun ByteArray.fixEmpty(): ByteArray = if (isNotEmpty()) this else almostEmptyArray


//
//// 四字节 时间
/*@SuppressLint("SimpleDateFormat")
fun ByteArray.readTimeBE(offset: Int = 0, pattern: String = "yyyy-MM-dd HH:mm:ss") =
    SimpleDateFormat(pattern).format(this.readUInt32BE(offset) * 1000)

@SuppressLint("SimpleDateFormat")
fun ByteArray.readTimeLE(offset: Int = 0, pattern: String = "yyyy-MM-dd HH:mm:ss") =
    SimpleDateFormat(pattern).format(this.readUInt32LE(offset) * 1000)*/


// 读取指定范围输出字符串
/*fun ByteArray.readStringBE(
    offset: Int,
    byteLength: Int,
    encoding: String = "hex",
    hasSpace: Boolean = encoding.toLowerCase() == "hex"
): String {
    return when (encoding.toLowerCase()) {
        "hex" -> this.readByteArrayBE(offset, byteLength).toHexString(hasSpace)
        "ascii" -> this.readByteArrayBE(offset, byteLength).map { it.toChar() }.joinToString(if (hasSpace) " " else "")
        else -> ""
    }
}

fun ByteArray.readStringLE(
    offset: Int,
    byteLength: Int,
    encoding: String = "hex",
    hasSpace: Boolean = encoding.toLowerCase() == "hex"
): String {
    return when (encoding.toLowerCase()) {
        "hex" -> this.readByteArrayLE(offset, byteLength).toHexString(hasSpace)
        "ascii" -> this.readByteArrayLE(offset, byteLength).map { it.toChar() }.joinToString(if (hasSpace) " " else "")
        else -> ""
    }
}*/


// 写入时间
/*@Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
@SuppressLint("SimpleDateFormat")
fun ByteArray.writeTimeBE(time: String, offset: Int = 0, pattern: String = "yyyy-MM-dd HH:mm:ss"): ByteArray {
    this.writeInt32BE(SimpleDateFormat(pattern).parse(time).time / 1000, offset)
    return this
}

@Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
@SuppressLint("SimpleDateFormat")
fun ByteArray.writeTimeLE(time: String, offset: Int = 0, pattern: String = "yyyy-MM-dd HH:mm:ss"): ByteArray {
    this.writeInt32LE(SimpleDateFormat(pattern).parse(time).time / 1000, offset)
    return this
}

// 指定位置写入ByteArray
@OptIn(ExperimentalStdlibApi::class)
fun ByteArray.writeByteArrayBE(byteArray: ByteArray, offset: Int = 0, length: Int = byteArray.size): ByteArray {
    this.writeStringBE(byteArray.toHexString(), offset, length)
    return this
}

@OptIn(ExperimentalStdlibApi::class)
fun ByteArray.writeByteArrayLE(byteArray: ByteArray, offset: Int = 0, length: Int = byteArray.size): ByteArray {
    this.writeStringLE(byteArray.toHexString(), offset, length)
    return this
}*/



// 指定位置写入String
/**
 * @str 写入的字符串
 * @encoding  Hex & ASCII
 */
/*fun ByteArray.writeStringBE(str: String, offset: Int = 0, encoding: String = "hex"): ByteArray {
    throwOffestError(this, offset)
    when (encoding.toLowerCase()) {
        "hex" -> {
            val hex = str.replace(" ", "")
            throwHexError(hex)
            for (i in 0 until hex.length / 2) {
                if (i + offset < this.size) {
                    this[i + offset] = hex.substring(i * 2, i * 2 + 2).toInt(16).toByte()
                }
            }
        }
        "ascii" -> {
            val hex = str.toCharArray().map { it.toInt() }.map { it.toString(16) }.joinToString("")
            this.writeStringBE(hex, offset, "hex")
        }
    }
    return this
}

// length: 写入长度
fun ByteArray.writeStringLE(str: String, offset: Int = 0, encoding: String = "hex"): ByteArray {
    when (encoding.toLowerCase()) {
        "hex" -> {
            val hex = str.reversalEvery2Charts()
            this.writeStringBE(hex, offset, encoding)
        }
        "ascii" -> {
            val hex = str.toCharArray().map { it.toInt() }.map { it.toString(16) }.joinToString("")
            this.writeStringLE(hex, offset, "hex")
        }
    }
    return this
}

fun ByteArray.writeStringBE(str: String, offset: Int, length: Int, encoding: String = "hex"): ByteArray {
    throwOffestError(this, offset, length)
    when (encoding.toLowerCase()) {
        "hex" -> {
            val hex = str.replace(" ", "").padStart(length * 2, '0').substring(0, length * 2)
            throwHexError(hex)
            this.writeStringBE(hex, offset)
        }
        "ascii" -> {
            val hex = str.toCharArray().map { it.toInt() }.map { it.toString(16) }.joinToString("")
            this.writeStringBE(hex, offset, length, "hex")
        }
    }
    return this
}

fun ByteArray.writeStringLE(str: String, offset: Int, length: Int, encoding: String = "hex"): ByteArray {
    when (encoding.toLowerCase()) {
        "hex" -> {
            val hex = str.reversalEvery2Charts().padEnd(length * 2, '0').substring(0, length * 2)
            this.writeStringBE(hex, offset, length, encoding)
        }
        "ascii" -> {
            val hex = str.toCharArray().map { it.toInt() }.map { it.toString(16) }.joinToString("")
            this.writeStringLE(hex, offset, length, "hex")
        }
    }
    return this
}*/

// 无符号int
private fun readUnsigned(byteArray: ByteArray, len: Int, offset: Int, littleEndian: Boolean): Long {
    var value = 0L
    for (count in 0 until len) {
        val shift = (if (littleEndian) count else (len - 1 - count)) shl 3
        value = value or (0xff.toLong() shl shift and (byteArray[offset + count].toLong() shl shift))
    }
    return value
}

/****  异常  ****/
private fun throwLenError(byteArray: ByteArray, byteLength: Int) {
    if (byteLength <= 0 || byteLength > 4) throw IllegalArgumentException("The value of \"byteLength\" is out of range. It must be >= 1 and <= 4. Received ${byteLength}")
    if (byteLength > byteArray.size) throw IllegalArgumentException("Attempt to write outside ByteArray bounds.")
}

private fun throwHexError(hex: String) {
    if (hex.length % 2 != 0) throw IllegalArgumentException("The value of \"hex\".length is out of range. It must be an even number")
}

fun throwOffestError(byteArray: ByteArray, offset: Int, length: Int = 1, byteLength: Int = 0) {
    if (offset > byteArray.size - length - byteLength) throw IllegalArgumentException("The value of \"offset\" is out of range. It must be >= 0 and <= ${byteArray.size - length - byteLength}. Received ${offset}")
}

fun ByteArray.hexDump( fromIndex: Int, length: Int):  String =
    HexUtil.hexDump(this, fromIndex, length)

fun ByteArray.prettyHexDump(fromIndex: Int = 0, length: Int = this.size): String =
    HexUtil.prettyHexDump(this, fromIndex, length)

fun ByteArray.toNoPrefixHexString() = toHexString( HexFormat{ number.prefix = "" })



